package main

import (
	"context"
	"crypto/ecdsa"
	"fmt"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
	"log"
	"math/big"
	"strings"
	"time"
)

func main() {
	client, err := ethclient.Dial("https://bsc-dataseed.binance.org/")
	if err != nil {
		log.Fatalf(err.Error())
	}

	blockNumber, err := client.BlockNumber(context.Background())
	if err != nil {
		log.Fatalf(err.Error())
	}
	block, err := client.BlockByNumber(context.Background(), big.NewInt(int64(blockNumber)))
	if err != nil {
		log.Fatalf(err.Error())
	}
	fmt.Println("Block number: ", block.Number().Uint64())
	for index, tx := range block.Transactions() {
		fmt.Printf("Transaction %d: %s\n", index, tx.Hash().Hex())
	}
}

/*
下面给出一个较为完整、可运行的思路和示例，帮助你用 Go 直接在 PancakeSwap 上做一次 BTCB → USDT 的交易。注意事项都在代码注释里给出。代码使用的是 Binance Smart Chain（BSC）主网的 PancakeSwap v2 路由合约和 BTCB/USDT 两个代币合约地址。你需要把地址替换成你实际使用的地址，并且务必避免把私钥直接写在代码里。

关键点回顾
- 交易需要两步：先授权路由合约代币额度（approve），再调用路由合约执行 swap。
- 路径（path）通常是 [tokenIn, tokenOut]，在 BTCB→USDT 的场景就是 [BTCB_ADDRESS, USDT_ADDRESS]。
- 你要有足够的 BTCB（tokenIn）的余额，以及 BNB 用于支付交易手续费（gas）。
- 需确认 BTCB 和 USDT 在 PancakeSwap 上有成对的流动性对，否则交易会失败。
- 在真实环境里不要把私钥硬编码，使用环境变量、Keystore 或硬件钱包签名。

准备工作
- 安装必要的库
- go-ethereum
- go-ethereum/crypto、bind、common 等
- 获取以下地址（请自行在 BscScan 等处确认并替换为你使用的地址）：
- PancakeSwap v2 Router 主地址（主网）: 0x10ED43C718714eb63d5aA57B78B54704E256024E
- BTCB（BTC on BSC）的 BEP-20 地址: 你自己的 BTCB 地址，例如 0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c
- USDT（USDT on BSC）的 BEP-20 地址: 例如 0x55d398326f99059fF775485246999027B26982566（请确认最新地址）
- 使用一个稳定的 BSC 节点 RPC（示例中使用公开节点，但在生产环境建议使用稳定的商用节点或自建节点）

详细示例代码（简化、可编译的版本，需要你替换地址和私钥）
注意：下面代码是示意性实现，若要投入生产，请做完整的错误处理、并发控制、 nonce 管理、价格滑点等处理。

所需 imports（请放在 go 文件头部）
 */
import (
"context"
"crypto/ecdsa"
"fmt"
"log"
"math/big"
"strings"
"time"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)

func main() {
	// 1) 连接 BSC 节点
	client, err := ethclient.Dial("https://bsc-dataseed.binance.org/")
	if err != nil {
		log.Fatalf("连接失败: %v", err)
	}

	// 2) 私钥（强烈建议不要硬编码，改为从环境变量/Keystore 加载）
	privateKeyHex := "YOUR_PRIVATE_KEY_HEX" // 替换为你的私钥（安全地获取）
	privateKey, err := crypto.HexToECDSA(privateKeyHex)
	if err != nil {
		log.Fatalf("无效私钥: %v", err)
	}

	// 3) 构造账户信息
	publicKey := privateKey.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		log.Fatal("无法从私钥获取公钥")
	}
	fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)

	// 4) 设置代币地址和路由地址（请替换为实际地址）
	btcbAddress := common.HexToAddress("BTCB_TOKEN_ADDRESS")   // 替换为 BTCB 地址
	usdtAddress := common.HexToAddress("USDT_TOKEN_ADDRESS")   // 替换为 USDT 地址
	routerAddress := common.HexToAddress("0x10ED43C718714eb63d5aA57B78B54704E256024E") // PancakeSwap v2 Router

	// 5) 获取 nonce、gasPrice、chainID，准备交易签名参数
	nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
	if err != nil {
		log.Fatalf("获取 nonce 失败: %v", err)
	}

	gasPrice, err := client.SuggestGasPrice(context.Background())
	if err != nil {
		log.Fatalf("获取 gasPrice 失败: %v", err)
	}

	chainID := big.NewInt(56) // BSC 主网的 chainID 为 56
	auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
	if err != nil {
		log.Fatalf("创建交易账户失败: %v", err)
	}
	auth.Nonce = big.NewInt(int64(nonce))
	auth.Value = big.NewInt(0)
	auth.GasPrice = gasPrice
	auth.GasLimit = uint64(400000) // 视实际情况调整

	// 6) 设置交易参数
	// amountIn: BTCB 的输入数量（单位是 wei，即 18 位小数）
	// 例如 0.5 BTCB： 0.5 * 10^18
	amountIn := new(big.Int)
	amountIn.SetString("500000000000000000", 10) // 0.5 BTCB，请根据实际情况设置

	// 路径：BTCB -> USDT
	path := []common.Address{btcbAddress, usdtAddress}

	// 期望输出最低数量（为了避免滑点，这里设为 0，生产请根据行情计算合适的 min 值）
	amountOutMin := big.NewInt(0)

	// 到账地址
	to := fromAddress

	// 截止时间
	deadline := big.NewInt(time.Now().Add(15 * time.Minute).Unix())

	// 7) 准备 ERC20 的最小 ABI（approve）
	erc20ABI := `[{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"type":"function"}]`
	// 8) 准备 Router 的最小 ABI（swapExactTokensForTokens）
	routerABI := `[{"constant":false,"inputs":[{"name":"amountIn","type":"uint256"},{"name":"amountOutMin","type":"uint256"},{"name":"path","type":"address[]"},{"name":"to","type":"address"},{"name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"name":"amounts","type":"uint256[]"}],"type":"function"}]`

	ercABI, err := abi.JSON(strings.NewReader(erc20ABI))
	if err != nil {
		log.Fatalf("ERC20 ABI 解析失败: %v", err)
	}
	routerABIObj, err := abi.JSON(strings.NewReader(routerABI))
	if err != nil {
		log.Fatalf("Router ABI 解析失败: %v", err)
	}

	// 9) 绑定合约并发送 approve 交易
	boundERC := bind.NewBoundContract(btcbAddress, ercABI, client, client, client)
	txApprove, err := boundERC.Transact(auth, "approve", routerAddress, amountIn)
	if err != nil {
		log.Fatalf("发送 approve 交易失败: %v", err)
	}
	fmt.Println("Approve 交易已发送，txHash:", txApprove.Hash().Hex())

	// 等待 approve 被挖矿
	receipt := waitForReceipt(client, txApprove.Hash())
	fmt.Println("Approve 已矿工封装在区块:", receipt.BlockNumber.Uint64())

	// 10) swap 交易
	boundRouter := bind.NewBoundContract(routerAddress, routerABIObj, client, client, client)
	txSwap, err := boundRouter.Transact(auth, "swapExactTokensForTokens", amountIn, amountOutMin, path, to, deadline)
	if err != nil {
		log.Fatalf("发送 swap 交易失败: %v", err)
	}
	fmt.Println("Swap 交易已发送，txHash:", txSwap.Hash().Hex())

	receipt2 := waitForReceipt(client, txSwap.Hash())
	fmt.Println("Swap 已矿工封装在区块:", receipt2.BlockNumber.Uint64())
}

func waitForReceipt(client *ethclient.Client, txHash common.Hash) *types.Receipt {
	for {
		receipt, err := client.TransactionReceipt(context.Background(), txHash)
		if err == nil && receipt != nil {
			return receipt
		}
		time.Sleep(time.Second)
	}
}

/*
说明与注意事项
- 地址替换：BTCB_ADDRESS、USDT_ADDRESS 替换为你实际使用的代币地址。请在 BscScan 等区块浏览器确认无误。
- 路由合约 ABI 简化：为了便于示例，使用了最小的 ERC20 approve 和 Router swap 函数 ABI。实际开发中，可以用 abigen 生成更完整、类型更清晰的 bindings。
- Slippage 与滑点控制：amountOutMin 设置为 0 只用于演示。实际使用时应根据市场价格和你的容忍滑点来计算一个合理的最小输出量，避免交易失败或损失。
- 代币小数与输入量：BTCB/USDT 等代币通常是 18 位小数。确保 amountIn、amountOutMin 等数值单位与代币小数一致。
- 安全性：私钥不要硬编码，务必使用安全的私钥管理方案（环境变量、Keystore、硬件钱包签名等）。
- 交易风险：在主网交易会产生实际资金损失风险，请在测试环境或小额交易后再上线。

如果你愿意，我也可以按你指定的 BTCB 和 USDT 地址，给出一个可直接编译运行的完整代码版本，包含地址替换、私钥获取、以及一个简单的价格滑点计算逻辑。你可以告诉我你打算交易的具体金额和允许的滑点比例，以及 BTCB/USDT 的确切合约地址。
 */